/**
 * マルチセッションtelnetサーバのスケッチ。
 * 最大8セッション。
 */
#include "boot/sketch.h"
#include "NyLPC_uipService.h"
#include "NyLPC_httpService.h"
#include <stdio.h>
//イーサネット用の初期化情報
const struct NyLPC_TEthAddr ethaddr=NyLPC_TEthAddr_pack(0x00,0x01,0x02,0x03,0x04,0x05);
const struct NyLPC_TIPv4Addr ipaddr=NyLPC_TIPv4Addr_pack(192,168,128,201);
const struct NyLPC_TIPv4Addr netmask=NyLPC_TIPv4Addr_pack(255,255,255,0);
const struct NyLPC_TIPv4Addr gateway=NyLPC_TIPv4Addr_pack(192,168,128,254);



#define NUM_OF_TH 8
struct TProc{
	NyLPC_TcThread_t th;
	NyLPC_TcTcpSocket_t socket;
	char rbuf[512];
}proc[NUM_OF_TH];


void setup(void)
{
	int i;
	//uipサービス初期化。いろいろ利用可能に。
	NyLPC_cUipService_initialize();
	for(i=0;i<NUM_OF_TH;i++){
		NyLPC_cThread_initialize(&(proc[i].th),160);
		NyLPC_cTcpSocket_initialize(&(proc[i].socket),proc[i].rbuf,512);
	}
}

/**
 * リクエストヘッダのパーサ
 */
NyLPC_TUInt16 parseReqHeader(NyLPC_TcHttpStream_t* i_st,struct NyLPC_TBasicHttpHeader* o_reqh)
{
	NyLPC_TUInt16 ret=200;
	NyLPC_TcHttpBasicHeaderParser_t hp;
	NyLPC_cHttpBasicHeaderParser_initialize(&hp);

	//ヘッダ解析
	if(!NyLPC_cHttpBasicHeaderParser_parse(&hp,&st,&reqheader)){
		ret=NyLPC_cHttpBasicHeaderParser_getStatusCode(&hp);
	}
	//ヘッダの内容確認
	if(reqheader.type!=NyLPC_THttpHeaderType_REQUEST){
		ret=400;
	}
	//GETだけネ
	if(reqheader.startline.req.method!=NyLPC_THttpMethodType_GET){
		ret=405;
	}
	NyLPC_cHttpBasicHeaderParser_finalize(&hp);
	return ret;
}

/**
 * 戻り値はpersistentConnectionが有効かどうか。
 */
NyLPC_TBool writeError(NyLPC_TcHttpStream_t* i_st,const struct NyLPC_TBasicHttpHeader* o_reqh,NyLPC_TUInt16 i_status,NyLPC_TBool i_is_persistent)
{
	char work[64];
	int l;
	NyLPC_TcHttpResponseWriter_t hw;
	l=sprintf(work,"<html><h1>Status %d</h1><hr/>"NyLPC_cHttpdConfig_SERVER"</html>",i_status);
	//ヘッダライタの生成
	NyLPC_cHttpResponseWriter_initialize(&hw,&i_st,&reqheader);
	//ヘッダ書込み
	if(!i_is_persistent){
		NyLPC_cHttpResponseWriter_setClose(&hw);
	}
	NyLPC_cHttpResponseWriter_setContentLength(&hw,(NyLPC_TUInt32)l);
	NyLPC_cHttpResponseWriter_writeHeader(&hw,ret);
	NyLPC_cHttpResponseWriter_writeMessageHeader(&hw,"Content-type","text/html");
	switch(NyLPC_cHttpResponseWriter_closeHeader(&hw)){
	case NyLPC_cHttpResponseWriter_ST_BODY:
		//Bodyの書込み
		if(NyLPC_cHttpResponseWriter_writeBody(&hw,work,l)){
			if(NyLPC_cHttpResponseWriter_closeBody(&hw)==NyLPC_cHttpResponseWriter_ST_HEAD){
				NyLPC_cHttpResponseWriter_finalize(&hw);
				return NyLPC_TBool_TRUE && i_is_persistent;
			}
		}
		break;
	default:
		//なんか変。
		break;
	}
	NyLPC_cHttpResponseWriter_finalize(&hw);
	return NyLPC_TBool_FALSE;
}


int server(void* p)
{
	struct TProc* proc=(struct TProc*)p;
	NyLPC_TInt32 s;
	NyLPC_TUInt16 ret;

	NyLPC_TcHttpStream_t st;
	NyLPC_TcHttpBasicHeaderParser_t hp;
	struct NyLPC_TBasicHttpHeader reqheader;
	NyLPC_TcHttpResponseWriter_t hw;
	NyLPC_TcTcpSocket_t ts;
	int body_len;
	if(!NyLPC_cTcpSocket_accept(&(proc->socket),3000)){
		return -1;
	}

	//TCPのオープン
	if(!NyLPC_cHttpStream_initialize(&st,&ts)){
		//エラー
	}
	for(;;){
		ret=parseReqHeader(&st,&reqheader);
		if(ret!=200){
			//エラーならエラーレスポンスを生成。
			writeError(&st,&reqheader,ret,NyLKPC_TBool_FALSE);
		}else{
			//コンテンツでも送ろうか。
		}
	}
	NyLPC_cHttpStream_finalize(&st);
	//TCPのクローズ
	NyLPC_cTcpSocket_close(&(proc->socket),10000);

	return;














void loop(void)
{
	NyLPC_TcIPv4Config_t config;
	NyLPC_TcTcpListener_t listener;
	int i;


	NyLPC_cIPv4Config_initialzeForEthernet(&config,&ethaddr,1480);
	NyLPC_cIPv4Config_setDefaultRoute(&config,&gateway);
	NyLPC_cIPv4Config_setIp(&config,&ipaddr,&netmask);


	NyLPC_cTcpListener_initialize(&listener,23);
	NyLPC_cUipService_start(&config);
	for(;;){
		//ターミネイト状態のタスクを検索
		for(i=0;i<NUM_OF_TH;i++){
			if(NyLPC_cThread_isTerminated(&(proc[i].th))){
				//リスニング
				if(!NyLPC_cTcpListener_listen(&listener,&(proc[i].socket),10000)){
					continue;
				}
				//タスク起動
				NyLPC_cThread_start(&(proc[i].th),server,&(proc[i]));
			}
		}
	}
	for(;;){}
}
